import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart' hide Response;
import 'package:kq_flutter_core_widget/config/kq_core_global.dart';
import 'package:kq_flutter_core_widget/network/cancel_token_group.dart';
import 'package:kq_flutter_core_widget/network/response.dart';
import 'package:kq_flutter_core_widget/resources/l10n/kq_core_s.dart';
import 'package:kq_flutter_core_widget/utils/ex/kq_ex.dart';
import 'package:kq_flutter_core_widget/utils/log.dart';
import 'package:kq_flutter_core_widget/utils/native/kq_notice_util.dart';
import 'package:stack_trace/stack_trace.dart';

/// 通用 Http 网络请求
class Http {
  /// HTTP请求
  ///
  /// 如果需要设置baseUrl，连接超时时间，读取超时时间等，则通过设置[options]参数即可。
  ///
  /// [transformer]允许在向从服务器'发送/接收'请求数据之前对'请求/接收'数据进行更改。
  /// transformer作用与拦截器类似，但是此对象一个Dio实例仅能配置一个且在所有拦截器之后
  /// 调用transformRequest，在所有拦截器之前调用transformResponse。
  ///
  /// [interceptors]则用于提供请求中几个阶段的拦截，增加一些自定义操作。已经实现了一些基本
  /// 的拦截器供外部使用，如[LoggerInterceptorsWrapper]、[GlobalParamsInterceptorsWrapper]。
  ///
  /// 请求异常时，外部要自定义请求错误消息，则通过[dioErrorHandler]即可。
  /// 在拿到请求结果后，如果请求失败，外部要自定义请求错误消息，则通过[errorCodeHandler]即可。
  Http(
      {BaseOptions? options,
      Transformer? transformer,
      Iterable<Interceptor>? interceptors,
      String Function(DioExceptionType type)? dioErrorHandler,
      String Function(int errorCode)? errorCodeHandler}) {
    _dio = Dio(options);
    if (interceptors != null) {
      _dio.interceptors.addAll(interceptors);
    }
    if (transformer != null) {
      _dio.transformer = transformer;
    }
    _dioErrorHandler = dioErrorHandler;
    _errorCodeHandler = errorCodeHandler;
  }

  /// HTTP请求,一些非必传参数提供默认实现
  ///
  /// 如果需要设置baseUrl，连接超时时间，读取超时时间等，则通过设置[options]参数即可。
  ///
  /// [transformer]允许在向从服务器'发送/接收'请求数据之前对'请求/接收'数据进行更改。
  /// transformer作用与拦截器类似，但是此对象一个Dio实例仅能配置一个且在所有拦截器之后
  /// 调用transformRequest，在所有拦截器之前调用transformResponse。
  ///
  /// [interceptors]则用于提供请求中几个阶段的拦截，增加一些自定义操作。已经实现了一些基本
  /// 的拦截器供外部使用，如[LoggerInterceptorsWrapper]、[GlobalParamsInterceptorsWrapper]。
  ///
  /// 在拿到请求结果后，如果请求失败，外部要自定义请求错误消息，则通过[errorCodeHandler]即可。
  Http.withDefault(
      {String? baseUrl,
      Transformer? transformer,
      Iterable<Interceptor>? interceptors,
      String Function(DioExceptionType type)? dioErrorHandler,
      String Function(int errorCode)? errorCodeHandler}) {
    _dio = Dio(getDefOptions()..baseUrl = baseUrl ?? "");
    if (interceptors != null) {
      _dio.interceptors.addAll(interceptors);
    }
    if (transformer != null) {
      _dio.transformer = transformer;
    }
    _dioErrorHandler = dioErrorHandler ?? defaultDioErrorHandler;
    _errorCodeHandler = errorCodeHandler ?? defaultErrorCodeHandler;
  }

  /// 创建 Dio 实例
  late final Dio _dio;

  /// 根据[DioExceptionType]返回对应错误消息
  String Function(DioExceptionType type)? _dioErrorHandler;

  /// 处理 Http 错误码,返回对应的错误消息
  String Function(int errorCode)? _errorCodeHandler;

  static const int statusOk = 200;

  /// 已接收请求，但处理尚未完成
  static const int _statusOkButNotComplete = 202;

  // 记录未分组的CancelToken
  static const _defaultCancelTokenGroup = "defaultCancelTokenGroup";

  // 记录所有请求用于取消请求的标识
  static final Map<String, List<CancelToken>> _cancelTokenList = {};

  /// 关闭所有请求
  static void closeAll() {
    for (var element in _cancelTokenList.values) {
      for (var value in element) {
        if (!value.isCancelled) {
          value.cancel("close all");
        }
      }
    }
    _cancelTokenList.clear();
  }

  /// 关闭指定分组请求
  static void closeByGroup(String groupName) {
    var list = _cancelTokenList[groupName];
    if (list == null || list.isEmpty) {
      return;
    }

    for (var value in list) {
      if (!value.isCancelled) {
        value.cancel("close group:$groupName");
      }
    }
    _cancelTokenList.remove(groupName);
  }

  static void _addCancelToken(CancelToken? cancelToken) {
    if (cancelToken == null) {
      return;
    }

    var group = cancelToken.groupName;
    List<CancelToken>? list;
    if (group == null) {
      list = _cancelTokenList[_defaultCancelTokenGroup];
      if (list == null) {
        list = [];
        _cancelTokenList[_defaultCancelTokenGroup] = list;
      }
    } else {
      list = _cancelTokenList[group];
      if (list == null) {
        list = [];
        _cancelTokenList[group] = list;
      }
    }
    list.add(cancelToken);
  }

  static void _removeCancelToken(CancelToken? cancelToken) {
    if (cancelToken == null) {
      return;
    }

    var group = cancelToken.groupName;
    if (group == null) {
      var list = _cancelTokenList[_defaultCancelTokenGroup];
      if (list != null) {
        list.remove(cancelToken);
      }
    } else {
      var list = _cancelTokenList[group];
      if (list != null) {
        list.remove(cancelToken);
      }
    }
  }

  /// 对请求数据/返回数据进行格式转化
  Transformer get transformer => _dio.transformer;

  /// 对请求数据/返回数据进行格式转化
  set transformer(Transformer transformer) {
    _dio.transformer = transformer;
  }

  /// 添加全局请求拦截器
  void addInterceptor(Interceptor interceptor) {
    _dio.interceptors.add(interceptor);
  }

  /// 移除全局请求拦截器
  void removeInterceptor(Interceptor interceptor) {
    _dio.interceptors.remove(interceptor);
  }

  /// 添加全局请求拦截器列表
  void addInterceptors(Iterable<Interceptor> interceptors) {
    _dio.interceptors.addAll(interceptors);
  }

  /// 执行http请求,返回原始数据。此请求是所有其它请求类型的原始方式，可以通过传参实现不同的请求类型。
  /// 比如需要实现GET请求，则method传GET，参数通过queryParameters传递
  ///
  /// [method]为请求类型，如：GET,POST,DELETE,PUT,HEAD,PATCH
  ///
  /// [queryParameters]为路径携带参数，如GET请求通过此参数传参
  ///
  /// [data]为POST等请求发送给服务器的数据，如JSON、字节流等
  ///
  /// [headers]为请求头
  ///
  /// [cancelToken]用于中断请求
  ///
  /// [onSendProgress]为发送数据进度回调，如上传文件。
  ///
  /// [onReceiveProgress]为接收数据进度回调，如下载文件。使用此方法下载文件，得到的为数据流。
  /// 如果想要直接保存为文件，请使用[download]
  ///
  /// [options]为请求的其它配置，如请求超时时间，接收超时，响应数据类型等
  ///
  /// 当请求被取消时，默认抛出错误类型为[DioExceptionType.cancel]的[DioException]。
  Future<Response<T>> rawRequest<T>(String path,
      {String? method,
      Map<String, dynamic>? queryParameters,
      Object? data,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress,
      HttpOptions? options}) async {
    return _requestWrapper(cancelToken, (cancelToken2) {
      var dioOptions = options?.toDioOptions() ?? Options();
      dioOptions.method = method;
      dioOptions.headers = headers;
      return _dio.request(path,
          queryParameters: queryParameters,
          data: data,
          cancelToken: cancelToken2,
          options: dioOptions,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress);
    });
  }

  Future<Response<T>> _requestWrapper<T>(CancelToken? cancelToken,
      Future<Response<T>> Function(CancelToken cancelToken) method) async {
    var finalCancelToken = cancelToken ?? CancelTokenEx.get();
    _addCancelToken(finalCancelToken);
    try {
      return await method.call(finalCancelToken);
    } finally {
      _removeCancelToken(finalCancelToken);
    }
  }

  /// 执行http请求，将得到的数据通过[responseMapper]转化为指定数据类型。
  ///
  /// [method]为请求类型，如：GET,POST,DELETE,PUT,HEAD,PATCH
  ///
  /// [queryParameters]为路径携带参数，如GET请求通过此参数传参
  ///
  /// [data]为POST等请求发送给服务器的数据，如JSON、字节流等
  ///
  /// [headers]为请求头
  ///
  /// [cancelToken]用于中断请求
  ///
  /// [onSendProgress]为发送数据进度回调，如上传文件。
  ///
  /// [onReceiveProgress]为接收数据进度回调，如下载文件。使用此方法下载文件，得到的为数据流。
  /// 如果想要直接保存为文件，请使用[download]
  ///
  /// [options]为请求的其它配置，如请求超时时间，接收超时，响应数据类型等
  ///
  /// 当请求被取消时，默认抛出错误类型为[DioExceptionType.cancel]的[DioException]。
  /// 如果不希望抛出异常，而是按照[ApiResponse]回调，则将[cancelByThrowException]置为false即可。
  /// 此时请求被取消时，不抛出异常，而是回调[ApiResponse.exceptionType] == [DioExceptionType.cancel]的response
  Future<ApiResponse<T>> request<T>(String path, String method,
      {Map<String, dynamic>? queryParameters,
      Object? data,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress,
      required ApiResponseMapper<T> responseMapper,
      bool cancelByThrowException = true,
      HttpOptions? options}) async {
    try {
      Response response = await rawRequest(path,
          method: method,
          queryParameters: queryParameters,
          data: data,
          headers: headers,
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress,
          options: options);

      if (response.statusCode != statusOk &&
          response.statusCode != _statusOkButNotComplete) {
        return ApiResponse(
            code: ApiResponse.failed,
            exceptionType: httpStatusCodeMap(response.statusCode),
            msg: _errorCodeHandler?.call(response.statusCode ?? 0) ?? '');
      }

      var mapData = mapResponseData(response.data);
      if (mapData == null) {
        return ApiResponse(
            code: ApiResponse.failed,
            exceptionType: DioExceptionType.badResponse,
            msg: _dioErrorHandler?.call(DioExceptionType.badResponse) ?? '');
      }
      return responseMapper.call(mapData);
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel && cancelByThrowException) {
        // 请求被取消
        rethrow;
      }
      e.printError();
      return ApiResponse(
          code: ApiResponse.failed,
          exceptionType: e.type,
          msg: _dioErrorHandler?.call(e.type) ?? '');
    } catch (e) {
      e.printError();
      return ApiResponse(
          code: ApiResponse.failed,
          exceptionType: DioExceptionType.unknown,
          msg: _dioErrorHandler?.call(DioExceptionType.unknown) ?? '');
    }
  }

  @protected
  DioExceptionType httpStatusCodeMap(int? errorCode) {
    var type = DioExceptionType.unknown;
    if (errorCode == null) {
      return type;
    }

    switch (errorCode) {
      case 403:
      case 501:
      case 502:
      case 503:
      case 505:
        type = DioExceptionType.connectionError;
        break;
      case 408:
      case 504:
        type = DioExceptionType.connectionTimeout;
        break;
    }
    return type;
  }

  /// 根据返回的数据，转化为Map对象
  @protected
  Map<String, dynamic>? mapResponseData(dynamic data) {
    try {
      if (data is Map<String, dynamic>) {
        return data;
      }

      if (data is String) {
        return json.decode(data);
      }

      return null;
    } catch (e) {
      return null;
    }
  }

  /// GET请求
  ///
  /// 请求[url]接口，[headers]指定请求头，[queryParameters]指定请求参数。
  ///
  /// 通过[responseMapper]对接口返回的数据进行转化，得到泛型[ApiResponse]<[T]>类对象。
  ///
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup]。
  ///
  /// 当请求被取消时，默认抛出错误类型为[DioExceptionType.cancel]的[DioException]。
  /// 如果不希望抛出异常，而是按照[ApiResponse]回调，则将[cancelByThrowException]置为false即可。
  /// 此时请求被取消时，不抛出异常，而是回调[ApiResponse.exceptionType] == [DioExceptionType.cancel]的response
  Future<ApiResponse<T>> get<T>(String url,
      {required ApiResponseMapper<T> responseMapper,
      Map<String, dynamic>? queryParameters,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      bool cancelByThrowException = true,
      HttpOptions? options}) {
    return request(
      url,
      "GET",
      responseMapper: responseMapper,
      queryParameters: queryParameters,
      headers: headers,
      cancelToken: cancelToken,
      cancelByThrowException: cancelByThrowException,
      options:
          options ?? HttpOptions(receiveTimeout: const Duration(seconds: 20)),
    );
  }

  /// POST请求
  ///
  /// 请求[url]接口，[headers]指定请求头，通过[data]传递数据到后台。
  ///
  /// 通过[responseMapper]对接口返回的数据进行转化，得到泛型[ApiResponse]<[T]>类对象。
  ///
  /// 如果需要取消接口，可设置[cancelToken]，想要批量取消请求，[cancelToken]可设置为[CancelTokenGroup]。
  ///
  /// 当请求被取消时，默认抛出错误类型为[DioExceptionType.cancel]的[DioException]。
  /// 如果不希望抛出异常，而是按照[ApiResponse]回调，则将[cancelByThrowException]置为false即可。
  /// 此时请求被取消时，不抛出异常，而是回调[ApiResponse.exceptionType] == [DioExceptionType.cancel]的response
  Future<ApiResponse<T>> post<T>(String url,
      {required ApiResponseMapper<T> responseMapper,
      Object? data,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken,
      bool cancelByThrowException = true,
      HttpOptions? options}) {
    return request(
      url,
      "POST",
      responseMapper: responseMapper,
      data: data,
      headers: headers,
      cancelToken: cancelToken,
      cancelByThrowException: cancelByThrowException,
      options:
          options ?? HttpOptions(receiveTimeout: const Duration(seconds: 20)),
    );
  }

  /// 下载文件
  ///
  /// [url]为下载文件的地址，[savePath]为将下载的文件存储到本地的路径。通过[progressCallback]监听下载进度
  Future<Response> download(String url, String savePath,
      {ProgressCallback? progressCallback,
      CancelToken? cancelToken,
      Map<String, dynamic>? queryParameters,
      Map<String, dynamic>? headers,
      Object? data,
      HttpOptions? options}) async {
    return _requestWrapper(cancelToken, (cancelToken2) async {
      var dioOptions = options?.toDioOptions() ?? Options();
      dioOptions.headers = headers;
      var response = await _dio.download(
        url,
        savePath,
        onReceiveProgress: progressCallback,
        cancelToken: cancelToken2,
        queryParameters: queryParameters,
        data: data,
        options: dioOptions,
      );
      return response;
    });
  }

  /// 默认配置
  static BaseOptions getDefOptions() {
    BaseOptions options = BaseOptions();
    options.connectTimeout = const Duration(milliseconds: 20 * 1000);
    return options;
  }

  static String defaultDioErrorHandler(DioExceptionType type) {
    switch (type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
        return KqCoreS.currentResource.connectTimeoutHint;
      case DioExceptionType.receiveTimeout:
        return KqCoreS.currentResource.responseTimeoutHint;
      default:
        return KqCoreS.currentResource.responseErrorHint;
    }
  }

  static String defaultErrorCodeHandler(int errorCode) {
    return '${KqCoreS.currentResource.httpErrorHint}$errorCode';
  }
}

/// Http请求配置
class HttpOptions {
  HttpOptions({
    Duration? sendTimeout,
    Duration? receiveTimeout,
    this.extra,
    this.responseType,
    this.contentType,
    this.validateStatus,
    this.receiveDataWhenStatusError,
    this.followRedirects,
    this.maxRedirects,
    this.persistentConnection,
    this.requestEncoder,
    this.responseDecoder,
    this.listFormat,
  })  : assert(receiveTimeout == null || !receiveTimeout.isNegative),
        _receiveTimeout = receiveTimeout,
        assert(sendTimeout == null || !sendTimeout.isNegative),
        _sendTimeout = sendTimeout;

  /// Timeout when sending data.
  ///
  /// [Dio] will throw the [DioException] with
  /// [DioExceptionType.sendTimeout] type when timed out.
  ///
  /// `null` meanings no timeout limit.
  Duration? get sendTimeout => _sendTimeout;
  Duration? _sendTimeout;

  set sendTimeout(Duration? value) {
    if (value != null && value.isNegative) {
      throw ArgumentError.value(value, 'sendTimeout', 'should be positive');
    }
    _sendTimeout = value;
  }

  /// Timeout when receiving data.
  ///
  /// The timeout represents the timeout during data transfer of each bytes,
  /// rather than the overall timing during the receiving.
  ///
  /// [Dio] will throw the [DioException] with
  /// [DioExceptionType.receiveTimeout] type when time out.
  ///
  /// `null` meanings no timeout limit.
  Duration? get receiveTimeout => _receiveTimeout;
  Duration? _receiveTimeout;

  set receiveTimeout(Duration? value) {
    if (value != null && value.isNegative) {
      throw ArgumentError.value(value, 'receiveTimeout', 'should be positive');
    }
    _receiveTimeout = value;
  }

  /// The request content-type.
  ///
  /// {@macro dio.interceptors.ImplyContentTypeInterceptor}
  String? contentType;

  /// Indicates the type of data that the server will respond with options.
  ///
  /// The default value is [ResponseType.json], [Dio] will parse response string
  /// to JSON object automatically when the content-type of response is
  /// [Headers.jsonContentType].
  ///
  /// If you want to receive response data with binary bytes, use `stream`.
  ///
  /// If you want to receive the response data with String, use `plain`.
  ///
  /// If you want to receive the response data with original bytes, use `bytes`.
  ResponseType? responseType;

  /// Defines whether the request is succeed with the given status code.
  /// The request will be treated as succeed if the callback returns true.
  ValidateStatus? validateStatus;

  /// Whether to retrieve the data if status code indicates a failed request.
  ///
  /// Defaults to true.
  bool? receiveDataWhenStatusError;

  /// An extra map that you can retrieve in [Interceptor], [Transformer]
  /// and [Response.requestOptions].
  ///
  /// The field is designed to be non-identical with [Response.extra].
  Map<String, dynamic>? extra;

  /// See [HttpClientRequest.followRedirects].
  ///
  /// Defaults to true.
  bool? followRedirects;

  /// The maximum number of redirects when [followRedirects] is `true`.
  /// [RedirectException] will be thrown if redirects exceeded the limit.
  ///
  /// Defaults to 5.
  int? maxRedirects;

  /// See [HttpClientRequest.persistentConnection].
  ///
  /// Defaults to true.
  bool? persistentConnection;

  /// The default request encoder is utf8encoder, you can set custom
  /// encoder by this option.
  RequestEncoder? requestEncoder;

  /// The default response decoder is utf8decoder, you can set custom
  /// decoder by this option, it will be used in [Transformer].
  ResponseDecoder? responseDecoder;

  /// Indicates the format of collection data in request query parameters and
  /// `x-www-url-encoded` body data.
  ///
  /// Defaults to [ListFormat.multi].
  ListFormat? listFormat;

  Options toDioOptions() => Options(
      sendTimeout: sendTimeout,
      receiveTimeout: receiveTimeout,
      extra: extra,
      responseType: responseType,
      contentType: contentType,
      validateStatus: validateStatus,
      receiveDataWhenStatusError: receiveDataWhenStatusError,
      followRedirects: followRedirects,
      maxRedirects: maxRedirects,
      persistentConnection: persistentConnection,
      requestEncoder: requestEncoder,
      responseDecoder: responseDecoder,
      listFormat: listFormat);
}

/// ApiResponse转换器，将接口数据转化为ApiResponse类
typedef ApiResponseMapper<T> = ApiResponse<T> Function(
    Map<String, dynamic> mapData);

/// 日志拦截器，打印请求的日志数据
class LoggerInterceptorsWrapper extends InterceptorsWrapper {
  LoggerInterceptorsWrapper(
      {this.printFullLogType = 0,
      this.transformer,
      this.logSourceTree = false,
      this.getSourceTreeLog});

  /// 是否打印完整的日志信息，对于POST请求，可能Body和返回数据较长，默认情况下会对内容进行裁断
  /// 0：不打印完整日志，超出限定长度截断，1：除Response外数据均打印完整日志数据，2：所有内容均打印完整日志
  final int printFullLogType;
  final Transformer? transformer;

  /// 是否打印调用栈
  final bool logSourceTree;

  /// 调用站过滤器，只打印调用栈以此字符开始的最后一条内容
  final String? Function(Trace trace)? getSourceTreeLog;

  static const int _limitLength = 800;

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    _printLog(response.requestOptions, response, null);
    super.onResponse(response, handler);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    _printLog(err.requestOptions, null, err);
    super.onError(err, handler);
  }

  void _printLog(
      RequestOptions options, Response? response, DioException? err) async {
    if (!kDebugMode) {
      return;
    }

    _print(
        '┌────────────────────────────────────────────────────────────────────────────────────────────────────────',
        false);
    if (logSourceTree) {
      StackTrace? sourceStackTrace;
      try {
        sourceStackTrace = options.sourceStackTrace;
      } catch (e) {
        //
      }

      if (sourceStackTrace != null) {
        var traceStr = getSourceTreeLog?.call(Trace.from(sourceStackTrace));
        if (traceStr != null) {
          _print(traceStr, printFullLogType > 0);
        }
      }
    }

    if (options.method == "GET") {
      var params = "?";
      options.queryParameters.forEach((key, value) {
        if (value != null) {
          if (params.length > 1) {
            params = "$params&$key=$value";
          } else {
            params = "$params$key=$value";
          }
        }
      });
      _print(
          " ${options.method}: ${options.path.startsWith('http') ? options.path : '${options.baseUrl}${options.path}'}${params.length > 1 ? params : ''}",
          printFullLogType > 0);
    } else {
      _print(
          " ${options.method}:${options.path.startsWith('http') ? options.path : '${options.baseUrl}${options.path}'}",
          printFullLogType > 0);
    }

    _print(" HEADERS: ${options.headers}", printFullLogType > 0);
    if (options.data != null) {
      dynamic body;
      if (transformer != null) {
        body = await transformer!.transformRequest(options);
        body = Uri.decodeComponent(body);
      } else {
        body = options.data;
      }

      _print(" BODY: $body", printFullLogType > 0);
    }

    if (response != null) {
      try {
        // 存在打印报错，导致调试连接断开，根据错误日志，应该是$response转字符时编码错误
        _print(' RESPONSE: $response', printFullLogType > 1);
      } catch (e) {
        //
      }
    } else if (err != null) {
      var response = err.response;
      if (response != null) {
        try {
          // 存在打印报错，导致调试连接断开，根据错误日志，应该是$response转字符时编码错误
          _print(' RESPONSE: ${err.response}', printFullLogType > 1);
        } catch (e) {
          //
        }
      }
      _print(' RESPONSE ERROR: ${err.message ?? ''}', printFullLogType > 1);
    }

    _print(
        '└────────────────────────────────────────────────────────────────────────────────────────────────────────',
        false);
  }

  void _print(String msg, bool printFullLog) {
    var length = msg.length;
    if (!printFullLog || length <= _limitLength) {
      Log.d(msg, tag: "http");
      return;
    }

    int index = 0;
    while (true) {
      var end = index + _limitLength;
      if (end >= length) {
        var str = msg.substring(index);
        Log.d(str, tag: "http");
        break;
      }

      var str = msg.substring(index, end);
      index = end;
      Log.d(str, tag: "http");
    }
  }

  /// 获取默认调用栈日志函数
  static String? getDefaultSourceTreeLog(Trace trace, String filter) {
    String? infos;
    for (var frame in trace.frames) {
      var str = frame.location;
      if (str.startsWith(filter)) {
        infos = str;
      }
    }
    return infos;
  }
}

/// 全局请求参数拦截器，在接口请求之前给接口加上全局请求参数
class GlobalParamsInterceptorsWrapper extends InterceptorsWrapper {
  Map<String, dynamic>? globalParams;

  /// 获取动态变更的全局参数
  Map<String, dynamic>? Function()? dynamicGlobalParams;

  GlobalParamsInterceptorsWrapper(
      {this.globalParams, this.dynamicGlobalParams});

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    var params = globalParams;
    var dynamicParams = dynamicGlobalParams?.call();
    if (params.isNullOrEmpty && dynamicParams.isNullOrEmpty) {
      super.onRequest(options, handler);
      return;
    }

    if (options.method == "GET") {
      Map<String, dynamic> map = {};
      if (params != null) {
        map.addAll(params);
      }

      if (dynamicParams != null) {
        map.addAll(dynamicParams);
      }

      if (map.isNotEmpty) {
        options.queryParameters.addAll(map);
      }
      options.queryParameters.removeWhere((key, value) => value == null);
    } else if (options.data == null) {
      Map<String, dynamic> map = {};
      if (params != null) {
        map.addAll(params);
      }

      if (dynamicParams != null) {
        map.addAll(dynamicParams);
      }
      map.removeWhere((key, value) => value == null);
      options.data = map.isEmpty ? null : map;
    } else if (options.data is Map) {
      // 不能去除Map泛型类型且泛型类型必须是<String, dynamic>
      // 可以查看Dio Transformer实现BackgroundTransformer，
      // 如果去除泛型且Content-Type不是application/json、text/json或结尾以+json结束
      // 最终传到后台的数据都是直接map.toString,这样可能与后台期望格式不一致，导致传参失败
      Map<String, dynamic> map = {};
      var data = options.data as Map;
      if (data.isNotEmpty && data is Map<String, dynamic>) {
        map.addAll(data);
        if (params != null) {
          for (var entry in params.entries) {
            if (!map.containsKey(entry.key)) {
              map[entry.key] = entry.value;
            }
          }
        }

        if (dynamicParams != null) {
          for (var entry in dynamicParams.entries) {
            if (!map.containsKey(entry.key)) {
              map[entry.key] = entry.value;
            }
          }
        }
      } else {
        if (params != null) {
          map.addAll(params);
        }

        if (dynamicParams != null) {
          map.addAll(dynamicParams);
        }
      }

      map.removeWhere((key, value) => value == null);
      options.data = map;
    }
    super.onRequest(options, handler);
  }
}

/// 接口调试模式拦截器，将接口请求记录发送通知栏
/// 通过 [KqCoreGlobal.enableApiTestMode] 开关
class ApiNoticeInterceptorsWrapper extends InterceptorsWrapper {
  ApiNoticeInterceptorsWrapper({this.transformer});

  final Transformer? transformer;

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    super.onRequest(options, handler);
    if (KqCoreGlobal.enableApiTestMode) {
      sendNotice(options);
    }
  }

  void sendNotice(RequestOptions options) async {
    String title = Get.currentRoute;
    String showContent = options.path;
    String copyContent = "";
    if (options.method == "GET") {
      var params = "?";
      options.queryParameters.forEach((key, value) {
        if (value != null) {
          if (params.length > 1) {
            params = "$params&$key=$value";
          } else {
            params = "$params$key=$value";
          }
        }
      });
      copyContent =
          "${options.method}:\n${options.path.startsWith('http') ? options.path : '${options.baseUrl}${options.path}'}${params.length > 1 ? params : ''}";
    } else {
      copyContent =
          "${options.method}:\n${options.path.startsWith('http') ? options.path : '${options.baseUrl}${options.path}'}";
    }
    if (options.headers.isNotEmpty) {
      copyContent = '$copyContent\n\n${"headers:${options.headers}"}';
    }
    if (options.data != null) {
      dynamic body;
      if (transformer != null) {
        body = await transformer!.transformRequest(options);
      } else {
        body = options.data;
      }

      copyContent = '$copyContent\n\n${"body:$body"}';
    }
    copyContent = '$copyContent\n\n${"page: ${Get.currentRoute}"}';
    KqNoticeUtil.showNoticeForApiTest(
        title: title, showContent: showContent, copyContent: copyContent);
  }
}
